import sys
import os
import logging, logging.handlers

import base64
from PIL import Image, ImageFont, ImageDraw 
from io import BytesIO

import numpy as np
import cv2
import time 
import math

file_path = os.path.abspath(__file__)
file_path_arr = os.path.split(file_path)
font_path = os.path.join(file_path_arr[0], 'simsun.ttc')

def is_crossing_line(p1, p2, line_start, line_end):
    # p1: 上一帧中心点
    # p2: 当前帧中心点
    # line_start, line_end: 终点线两端点
    def cross(a, b, c):
        return (b[0]-a[0])*(c[1]-a[1]) - (b[1]-a[1])*(c[0]-a[0])
    
    ab = (line_end[0] - line_start[0], line_end[1] - line_start[1])
    ap = (p1[0] - line_start[0], p1[1] - line_start[1])
    bp = (p2[0] - line_start[0], p2[1] - line_start[1])

    cp1 = cross(line_start, line_end, p1)
    cp2 = cross(line_start, line_end, p2)

    if cp1 * cp2 < 0:
        return True
    return False

def is_point_near_segment(point, segment_start, segment_end, threshold=10):
    """
    判断点是否接近线段（点到线的距离小于阈值）。
    
    参数：
        point (tuple): 点的坐标 (px, py)。
        segment_start (tuple): 线段起点的坐标 (x1, y1)。
        segment_end (tuple): 线段终点的坐标 (x2, y2)。
        threshold (float): 距离阈值，默认为 10 像素。
    
    返回：
        bool: 如果点接近线段返回 True，否则返回 False。
    """
    px, py = point
    x1, y1 = segment_start
    x2, y2 = segment_end

    # 1. 计算点到直线的距离
    # 直线方程：(y2 - y1)x - (x2 - x1)y + (x2 * y1 - x1 * y2) = 0
    A = int(y2 - y1)
    B = -int((x2 - x1))
    C = int(x2 * y1) - int(x1 * y2)

    # 点到直线的距离公式
    distance = abs(A * px + B * py + C) / math.sqrt(A**2 + B**2)

    # 如果距离大于阈值，直接返回 False
    if distance > threshold:
        return False

    # 2. 检查点的投影是否落在线段范围内
    # 投影比例 t = ((px - x1) * (x2 - x1) + (py - y1) * (y2 - y1)) / |AB|^2
    dx = x2 - x1
    dy = y2 - y1
    segment_length_squared = dx**2 + dy**2
    if segment_length_squared == 0:  # 避免除以零（线段退化为点）
        return math.hypot(px - x1, py - y1) <= threshold

    t = ((px - x1) * dx + (py - y1) * dy) / segment_length_squared
    if 0 <= t <= 1:  # 投影落在线段范围内
        return True

    # 3. 如果投影不在线段范围内，检查点是否靠近线段端点
    # 计算点到线段两个端点的距离
    dist_to_start = math.hypot(px - x1, py - y1)
    dist_to_end = math.hypot(px - x2, py - y2)

    return min(dist_to_start, dist_to_end) <= threshold, distance

def is_point_on_segment(px, py, x1, y1, x2, y2):
    # 首先判断点是否在线段所在的直线上
    if x1 == x2:
        # 竖直线段，判断点的x坐标是否与线段x坐标相同，且y坐标在y1和y2之间
        return px == x1 and min(y1, y2) <= py <= max(y1, y2)
    elif y1 == y2:
        # 水平线段，判断点的y坐标是否与线段y坐标相同，且x坐标在x1和x2之间
        return py == y1 and min(x1, x2) <= px <= max(x1, x2)
    else:
        # 斜线段，计算斜率
        slope = (y2 - y1) / (x2 - x1)
        
        # 判断点是否满足直线方程（考虑浮点数精度问题）
        epsilon = 1e-9
        on_line = abs(py - y1 - slope * (px - x1)) < epsilon
        
        # 判断点的x坐标和y坐标是否在线段范围内
        within_x_range = min(x1, x2) <= px <= max(x1, x2)
        within_y_range = min(y1, y2) <= py <= max(y1, y2)
        
        # 返回结果：点既在直线上，又在线段范围内
        return on_line and within_x_range and within_y_range
    

def is_point_crossed_line_segment(p1, p2, prev_p, curr_p):
    """
    判断一个运动的点是否跑过了一条线段。

    参数：
        p1: 线段的第一个端点，格式为 (x1, y1)。
        p2: 线段的第二个端点，格式为 (x2, y2)。
        prev_p: 点的前一时刻位置，格式为 (xp_prev, yp_prev)。
        curr_p: 点的当前时刻位置，格式为 (xp_curr, yp_curr)。

    返回：
        True: 点跑过了线段。
        False: 点没有跑过线段。
    """
    # 提取点的坐标
    x1, y1 = p1
    x2, y2 = p2
    xp_prev, yp_prev = prev_p
    xp_curr, yp_curr = curr_p

    # 向量 AB 和 AP 的叉积函数
    def cross_product(ax, ay, bx, by):
        return ax * by - ay * bx

    # 计算线段向量 AB
    ab_x = x2 - x1
    ab_y = y2 - y1

    # 计算前一时刻和当前时刻点相对于线段的叉积
    cross_prev = cross_product(ab_x, ab_y, xp_prev - x1, yp_prev - y1)
    cross_curr = cross_product(ab_x, ab_y, xp_curr - x1, yp_curr - y1)

    # 如果叉积符号相反，则说明点跑过了线段
    if cross_prev * cross_curr < 0:
        return True

    return False



def get_logger(log_dir, log_file='log.txt'):
    if not os.path.exists(log_dir):
        os.makedirs(log_dir)

    filename = os.path.join(log_dir, log_file)
    log_format = '%(asctime)s %(message)s'

    logger = logging.getLogger(log_file.split('.')[0])
    logger.setLevel(level=logging.INFO)

    # file_handler = logging.FileHandler(filename)
    file_handler = logging.handlers.RotatingFileHandler(filename, maxBytes=100*1024*1024, backupCount=9)
    file_handler.setFormatter(logging.Formatter(log_format))
    logger.addHandler(file_handler)    

    stream_handler = logging.StreamHandler(sys.stdout)
    stream_handler.setFormatter(logging.Formatter(log_format))
    logger.addHandler(stream_handler)

    return logger

def convert_image_2_base64_str(image,format='JPEG'):
    img=Image.fromarray(image[:,:,::-1])
    output_buffer = BytesIO()
    img=img.convert('RGB')
    img.save(output_buffer, format=format)
    byte_data = output_buffer.getvalue()
    return base64.b64encode(byte_data).decode('utf-8')

def drawImg(img_opencv, box_arr, kps_arr, text_arr, color_arr, text_size=20):
    for box, kps, color in zip(box_arr, kps_arr, color_arr):
        cv2.rectangle(img_opencv, box[0:2], box[2:4], color, 2)
        for kp in kps: cv2.circle(img_opencv, kp, 4, color, -1)

    img_pil = Image.fromarray(img_opencv)
    font = ImageFont.truetype(font_path, text_size)

    draw = ImageDraw.Draw(img_pil)
    for box, text, color in zip(box_arr, text_arr, color_arr):
        if not isinstance(text, np.unicode):
            text = text.decode('utf8')
        draw.text((box[0], box[1]-text_size), text, font=font, fill=color)
    return np.asarray(img_pil)

class HeartBeat():
    def __init__(self, id, threshold=1*60):
        self.id = id 
        self.threshold = threshold 
        self.last_beat = int(time.time())

    def is_alive(self):
        now = int(time.time())
        return now - self.last_beat < self.threshold

    def beat(self):
        self.last_beat = int(time.time())

class AverageMeter():
    def __init__(self, len=100):
        self.val = 0
        self.sum = 0
        self.avg = 0
        self.count = 0

        self.len = len
        self.arr = []

    def update(self, val):
        self.val = val
        self.sum += val
        self.count += 1

        if self.len > 0:
            self.arr.append(val)
            if len(self.arr) > self.len:
                self.sum -= self.arr[0]
                self.arr.pop(0)
                self.count -= 1

        self.avg = self.sum / self.count
